#include "tcpserver.hpp"
#include <memory>
#include "err.hpp"
#include "Protocol.hpp"

static void usage(std::string proc)
{
    std::cerr << "Usage:\n\t" << proc << " port" << "\n\n";
}

// 计算
bool cal(const Request &req, Response &resp)
{
    // req已经有结构化完成的数据啦，你可以直接使用
    resp.exitcode = OK;
    resp.result = OK;

    switch (req.op)
    {
    case '+':
        resp.result = req.x + req.y;
        break;
    case '-':
        resp.result = req.x - req.y;
        break;
    case '*':
        resp.result = req.x * req.y;
        break;
    case '/':
    {
        if (req.y == 0)
            resp.exitcode = DIV_ZERO;
        else
            resp.result = req.x / req.y;
    }
    break;
    case '%':
    {
        if (req.y == 0)
            resp.exitcode = MOD_ZERO;
        else
            resp.result = req.x % req.y;
    }
    break;
    default:
        resp.exitcode = OP_ERROR;
        break;
    }

    return true;
}

// 收到数据 根据协议解析出来
void _servers(tcpserver_ns::Connection *conn)
{
    std::string onepackage;
    while (ParseOnePackage(conn->_inbuffer, &onepackage))
    {
        // 读取到了一个完整的报文 ->再向上交付

        // 去掉协议头
        std::string reqstr;
        if (!deLength(onepackage, &reqstr))
            return;

        // 根据reqstr构建 Request (结构化数据)  : 反序列化
        Request req;
        if (!req.deserialize(reqstr))
        {
            return;
        }

        // 计算
        Response resp;
        cal(req, resp);

        //序列化
        std::string respStr;
        resp.serialize(&respStr);

        //加上协议头 //不能在这里直接发送, 而是添加到_outbuffer中
        conn->_outbuffer += enLength(respStr);

        //服务器刚刚开始启动的时候, 或者很多情况下,发送事件一直是就绪的, 理论上可以直接发送
        //难免的会有情况不就绪, 所以每一个sock都要有自己的发送缓冲区 _outbuffer;
        //注定你要将sock的EPOLLOUT事件注册到epoll模型中,
        //如果写事件一直长设, 他就会一直返回,大部分情况都是可以写的, 即使缓冲区里面没用数据也要去处理写事件
        //所以对于读事件要长设epoll模型中
        //而对于写事件要按需设置进epoll模型

        //所以就是处理完成之后,直接先发一次,没发完就将写入注册进入epoll模型中, 关注写事件
        if(conn->_sender){
            conn->_sender(conn);
        }
    }
}

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        usage(argv[0]);
        exit(USAGE_ERR);
    }
    uint16_t port = atoi(argv[1]);

    std::unique_ptr<tcpserver_ns::TcpServer> epollsvr(new tcpserver_ns::TcpServer(_servers, port));
    epollsvr->initServer();

    epollsvr->Dispatcher();

    return 0;
}